/*
  Copyright 2011 the JSDoc Authors.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

describe('@param tag', () => {
  const docSet = jsdoc.getDocSetFromFile('test/fixtures/paramtag.js');
  const docSet2 = jsdoc.getDocSetFromFile('test/fixtures/paramtag2.js');

  it('When a symbol has an @param tag with a type before the name, the doclet has a params property that includes that param.', () => {
    const find = docSet.getByLongname('find')[0];

    expect(find.params).toBeArrayOfSize(1);
    expect(find.params[0].type.names.join(', ')).toBe('String, Array<String>');
    expect(find.params[0].name).toBe('targetName');
    expect(find.params[0].description).toBe('The name (or names) of what to find.');
  });

  it('When a symbol has an @param tag with only a type and name, the doclet has a params property that includes that param.', () => {
    const bind = docSet.getByLongname('bind')[0];

    expect(bind.params).toBeArrayOfSize(1);
    expect(bind.params[0].type.names.join(', ')).toBe('function');
    expect(bind.params[0].name).toBe('callback');
    expect(bind.params[0].description).toBeUndefined();
  });

  it('When a symbol has an @param tag with only a type, the doclet has a params property that includes that param.', () => {
    const unbind = docSet.getByLongname('unbind')[0];

    expect(unbind.params).toBeArrayOfSize(1);
    expect(unbind.params[0].type.names.join(', ')).toBe('function');
    expect(unbind.params[0].description).toBeUndefined();
  });

  it('When a symbol has an @param tag with no type, the doclet has a params property that includes that param.', () => {
    const getElement = docSet.getByLongname('getElement')[0];

    expect(getElement.params).toBeArrayOfSize(1);
    expect(getElement.params[0].type).toBeUndefined();
    expect(getElement.params[0].name).toBe('id');
    expect(getElement.params[0].description).toBe('The id of the element.');
  });

  it('When a symbol has an @param tag with a non-alpha name like "...", the doclet has a params property that includes that param.', () => {
    const combine = docSet.getByLongname('combine')[0];

    expect(combine.params).toBeArrayOfSize(1);
    expect(combine.params[0].type).toBeUndefined();
    expect(combine.params[0].name).toBe('...');
    expect(combine.params[0].description).toBe('Two or more elements.');
  });

  it('When a symbol has an @param tag with name followed by a dash, the doclet has a params property that includes that param.', () => {
    const split = docSet.getByLongname('split')[0];

    expect(split.params).toBeArrayOfSize(1);
    expect(split.params[0].type).toBeUndefined();
    expect(split.params[0].name).toBe('delimiter');
    expect(split.params[0].description).toBe('What to split on.');
  });

  it('When a symbol has an @param tag with no name or type, the doclet has a params property that includes that param.', () => {
    const commit = docSet.getByLongname('commit')[0];

    expect(commit.params).toBeArrayOfSize(1);
    expect(commit.params[0].type).toBeUndefined();
    expect(commit.params[0].description).toBe('If true make the commit atomic.');
  });

  it('When a symbol has a @param tag with no type but a name that indicates a default value or optional type, this is copied over to the params property.', () => {
    const request = docSet.getByLongname('request')[0];

    expect(request.params).toBeArrayOfSize(1);
    expect(request.params[0].type).toBeUndefined();
    expect(request.params[0].name).toBe('async');
    expect(request.params[0].defaultvalue).toBeTrue();
    expect(request.params[0].optional).toBeTrue();
    expect(request.params[0].description).toBe('whether to be asynchronous');
  });

  it('When a symbol has a @param tag with no name, the doclet includes the param name from the code', () => {
    const commit = docSet.getByLongname('commit')[0];

    expect(commit.params).toBeArrayOfSize(1);
    expect(commit.params[0].name).toBe('atomic');
  });

  it('When a symbol has a @param tag with no name, and the symbol is part of an assignment expression, the doclet includes the param name from the code', () => {
    const classOpen = docSet.getByLongname('MySocket#open')[0];
    const moduleOpen = docSet2.getByLongname('module:mysocket.open')[0];

    expect(classOpen.params).toBeArrayOfSize(2);
    expect(classOpen.params[0].name).toBe('hostname');
    expect(classOpen.params[1].name).toBe('port');

    expect(moduleOpen.params).toBeArrayOfSize(2);
    expect(moduleOpen.params[0].name).toBe('hostname');
    expect(moduleOpen.params[1].name).toBe('port');
  });

  it('When a symbol has a @param tag with an invalid type expression, the JSDoc comment is ignored.', () => {
    const badDocSet = jsdoc.getDocSetFromFile('test/fixtures/paramtaginvalidtype.js');
    const test = badDocSet.getByLongname('Test#test')[0];

    expect(test).toBeObject();
    expect(test.meta).toBeObject();
    expect(test.meta.filename).toBe('[[string0]]');
    expect(test.description).toBeUndefined();
  });
});
